package com.fintech.pangu.apollo.autorefresh;

import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;

import java.util.Set;

/**
 * @Description:
 */
public class AutoRefreshChangeRebinder implements ApplicationContextAware, ApplicationListener<AutoRefreshChangeEvent> {

    private ConfigurationPropertiesBindingPostProcessor binder;

    private ApplicationContext applicationContext;

    public AutoRefreshChangeRebinder(ConfigurationPropertiesBindingPostProcessor binder) {
        this.binder = binder;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    @Override
    public void onApplicationEvent(AutoRefreshChangeEvent event) {
        rebind(event.getBeanNames());
    }

    public void rebind(Set<String> beanNames) {
        if (beanNames != null && !beanNames.isEmpty()){
            for (String beanName : beanNames){
                rebind(beanName);
            }
        }
    }

    public boolean rebind(String name) {
        if (this.applicationContext != null) {
            try {
                Object bean = this.applicationContext.getBean(name);
                if (AopUtils.isCglibProxy(bean)) {
                    bean = getTargetObject(bean);
                }
                this.binder.postProcessBeforeInitialization(bean, name);
                this.applicationContext.getAutowireCapableBeanFactory()
                        .initializeBean(bean, name);
                return true;
            }
            catch (RuntimeException e) {
                throw e;
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    private static <T> T getTargetObject(Object candidate) {
        try {
            if (AopUtils.isAopProxy(candidate) && (candidate instanceof Advised)) {
                return (T) ((Advised) candidate).getTargetSource().getTarget();
            }
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to unwrap proxied object", ex);
        }
        return (T) candidate;
    }

}
